package com.qipay.notify.impl;

import com.alibaba.fastjson.JSONObject;
import com.qipay.baas.model.Cp;
import com.qipay.baas.model.NotifyLog;
import com.qipay.baas.model.NotifyLogExample;
import com.qipay.baas.model.Order;
import com.qipay.cp.CpService;
import com.qipay.mapper.NotifyLogMapper;
import com.qipay.notify.NotifyLogService;
import com.qipay.scalar.type.ApiStatusEnum;
import com.qipay.scalar.type.NotifyLogStateEnum;
import com.qipay.utils.ResultUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

@Service
public class NotifyLogServiceImpl implements NotifyLogService {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    @Value("${api.response.sign.name:}")
    String apiResponseSignName;

    @Value("${api.response.key.name:}")
    String apiResponseKeyName;

    private int[] NotifyFailRate = {
            0, 15, 15, 30, 180, 1800, 1800, 1800, 1800, 3600
    };

    @Autowired
    NotifyLogMapper notifyLogMapper;

    @Autowired
    CpService cpService;

    @Override
    @Transactional
    public void setOrderSuccess(Order order, Cp cp) throws IOException {
        NotifyLog log = new NotifyLog();
        log.setNotifyUrl(order.getNotifyUrl());
        log.setOpType((byte) 1);
        log.setState((byte) 0);
        log.setTradeNo(order.getTradeNo());
        log.setFailCount((byte) 0);
        log.setCreateTime(new Date());
        log.setLastNotifyTime(new Date());

        Map<String, Object> msgMaps = new HashMap<>();
        msgMaps.put("status", ApiStatusEnum.SUCCESS.getCode());
        msgMaps.put("mch_id", cp.getAppId());
        msgMaps.put("trade_type", order.getChannelType());
        msgMaps.put("out_trade_no", order.getOutTradeNo());
        msgMaps.put("total_fee", order.getAmount());

        String msg = JSONObject.toJSONString(ResultUtils.signResult(
                msgMaps,cp.getSecretId(),"sign","key"));
        log.setNotifyMsg(msg);
        if (notifyLogMapper.selectByTradeNo(order.getTradeNo()) == 0) {
            notifyLogMapper.insert(log);
        } else {
            notifyLogMapper.updateByPrimaryKey(log);
        }
    }

    @Override
    @Transactional
    public List<NotifyLog> getNotifyList(int size) {
        NotifyLogExample notifyLogExample = new NotifyLogExample();
        notifyLogExample.setLimit(size);
        notifyLogExample.setOrderByClause("last_notify_time");
        notifyLogExample.createCriteria().andLastNotifyTimeLessThan(new Date())
                .andStateNotEqualTo(NotifyLogStateEnum.SUCCESS.getType())
                .andStateNotEqualTo(NotifyLogStateEnum.FAIL.getType())
                .andFailCountLessThan((byte) NotifyFailRate.length);
        return notifyLogMapper.selectByExample(notifyLogExample);
    }

    @Override
    @Async
    @Transactional
    public void send(NotifyLog notifyLog) {
        if (notifyLog == null) {
            return;
        }
        if (Objects.equals(notifyLog.getState(), NotifyLogStateEnum.SUCCESS.getType())) {
            return;
        }
        if (notifyLog.getFailCount() >= NotifyFailRate.length) {
            notifyLog.setState(NotifyLogStateEnum.FAIL.getType());
            notifyLogMapper.updateByPrimaryKey(notifyLog);
            return;
        }

        if(StringUtils.equalsIgnoreCase(notifyLog.getNotifyUrl(), "none")) {
            notifyLog.setState(NotifyLogStateEnum.SUCCESS.getType());
            notifyLogMapper.updateByPrimaryKey(notifyLog);
            return;
        }

        if (notifyLog.getOpType() == 1) {
            String resp = sendPostForm(JSONObject.parseObject(notifyLog.getNotifyMsg()), notifyLog.getNotifyUrl());
            notifyLog.setRsp(resp);
            if (StringUtils.isNotBlank(resp) && StringUtils.equalsIgnoreCase(resp.toLowerCase(), "success")) {
                notifyLog.setState(NotifyLogStateEnum.SUCCESS.getType());
            } else {
                notifyLog.setState(NotifyLogStateEnum.UNSUCCESS.getType());
                if (notifyLog.getFailCount() >= NotifyFailRate.length - 1) {
                    notifyLog.setState(NotifyLogStateEnum.FAIL.getType());
                } else {
                    notifyLog.setFailCount((byte) (notifyLog.getFailCount() + 1));
                    notifyLog.setLastNotifyTime(
                            org.apache.commons.lang3.time.DateUtils.addSeconds(
                                    new Date(),
                                    NotifyFailRate[notifyLog.getFailCount()]
                            )
                    );
                }
            }
            notifyLogMapper.updateByPrimaryKey(notifyLog);
        }
    }

    protected String sendPost(String strBody, String url) {
        final HttpParams httpParams = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(httpParams, 6000);
        HttpConnectionParams.setSoTimeout(httpParams,6000);
        HttpClient client = new DefaultHttpClient(httpParams);

        String result = null;

        HttpPost post = new HttpPost(url);
        try {
            if (null != strBody) {
                logger.info(getClass().getName() +
                        " pay sendPost request body : " +
                        strBody + ", request url:" + url);
                StringEntity entity = new StringEntity(strBody, "utf-8");
                entity.setContentEncoding("UTF-8");
                entity.setContentType("application/xml");
                post.setEntity(entity);
            }

            HttpResponse resultResp = client.execute(post);

            if (resultResp.getStatusLine().getStatusCode() == 200) {
                try {
                    result = EntityUtils.toString(resultResp.getEntity(), "utf8");
                    logger.info(getClass().getName() +
                            "pay sendPost response body : " + result + ", request url:" + url);
                } catch (Exception e) {
                    logger.error(e.toString());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.toString());
        }
        return result;
    }
    public static String sendPostForm(Map<String, Object> params,String url) {
        URL u = null;
        HttpURLConnection con = null;
        // 构建请求参数
        StringBuffer sb = new StringBuffer();
        String sbStr = "";
        if (params != null) {
            for (Map.Entry<String, Object> e : params.entrySet()) {
                sb.append(e.getKey());
                sb.append("=");
                sb.append(e.getValue());
                sb.append("&");
            }
            sbStr = sb.substring(0, sb.length() - 1);
        }
        System.out.println("send_url:" + url);
        System.out.println("send_data:" + sbStr);
        // 尝试发送请求
        try {
            u = new URL(url);
            con = (HttpURLConnection) u.openConnection();
            //// POST 只能为大写，严格限制，post会不识别
            con.setRequestMethod("POST");
            con.setDoOutput(true);
            con.setDoInput(true);
            con.setUseCaches(false);
            con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            OutputStreamWriter osw = new OutputStreamWriter(con.getOutputStream(), "UTF-8");
            osw.write(sbStr);
            osw.flush();
            osw.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (con != null) {
                con.disconnect();
            }
        }

        // 读取返回内容
        StringBuffer buffer = new StringBuffer();
        try {
            //一定要有返回值，否则无法把请求发送给server端。
            BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
            String temp;
            while ((temp = br.readLine()) != null) {
                buffer.append(temp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return buffer.toString();
    }
}
